<!DOCTYPE html>
<html lang="zn">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文件断点续传</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.0/spark-md5.min.js"></script>
  <style>
    body {
      margin: 0;
      font-size: 16px;
      background: #f8f8f8;
    }

    h1,
    h2,
    h3,
    h4,
    h5,
    h6,
    p {
      margin: 0;
    }

    /* * {
    outline: 1px solid pink;
} */

    .upload {
      box-sizing: border-box;
      margin: 30px auto;
      padding: 15px 20px;
      width: 500px;
      height: auto;
      border-radius: 15px;
      background: #fff;
    }

    .upload h3 {
      font-size: 20px;
      line-height: 2;
      text-align: center;
    }

    .upload .upload-file {
      position: relative;
      margin: 30px auto;
    }

    .upload .upload-file label {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 150px;
      border: 1px dashed #ccc;
    }

    .upload .upload-file input {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
    }

    .upload-progress {
      display: flex;
      align-items: center;
    }

    .upload-progress p {
      position: relative;
      display: inline-block;
      flex: 1;
      height: 15px;
      border-radius: 10px;
      background: #ccc;
      overflow: hidden;
    }

    .upload-progress p span {
      position: absolute;
      left: 0;
      top: 0;
      width: 0;
      height: 100%;
      background: linear-gradient(to right bottom, rgb(163, 76, 76), rgb(231, 73, 52));
      transition: all .4s;
    }

    .upload-link {
      margin: 30px auto;
    }

    .upload-link a {
      text-decoration: none;
      color: rgb(6, 102, 192);
    }

    @media all and (max-width: 768px) {
      .upload {
        width: 300px;
      }
    }
  </style>
</head>

<body>
  <div class="upload">
    <h3>普通上传</h3>
    <form>
      <div class="upload-file">
        <label for="file">请选择文件</label>
        <input type="file" name="file" id="file" accept="image/*">
      </div>
      <div class="upload-progress">
        当前进度：
        <p>
          <span style="width: 0;" id="current"></span>
        </p>
      </div>
      <div class="upload-link">
        文件地址：<a id="links" href="javascript:void();" target="_blank">文件链接</a>
      </div>
    </form>
  </div>
  <div class="upload">
    <h3>大文件上传</h3>
    <form>
      <div class="upload-file">
        <label for="file">请选择文件</label>
        <input type="file" name="file" id="big-file" accept=".mp4">
      </div>
      <div class="upload-progress">
        当前进度：
        <p>
          <span style="width: 0;" id="big-current"></span>
        </p>
      </div>
      <div class="upload-link">
        文件地址：<a id="big-links" href="" target="_blank">文件链接</a>
      </div>
    </form>
  </div>
</body>
<script>
  // 获取元素
  const file = document.querySelector('#file');
  let current = document.querySelector('#current');
  let links = document.querySelector('#links');
  let baseUrl = 'http://localhost:3333';


  const bigFile = document.querySelector('#big-file');
  let bigCurrent = document.querySelector('#big-current');
  let bigLinks = document.querySelector('#big-links');
  let fileArr = [];
  let md5Val = '';
  let ext = '';


  // 监听文件事件
  file.addEventListener('change', (e) => {
    console.log(e.target.files);
    let file = e.target.files[0];
    // if (file.type.indexOf('image') == -1) {
    //   return alert('文件格式只能是图片！');
    // }
    // if ((file.size / 1000) > 100) {
    //   return alert('文件不能大于100KB！');
    // }
    links.href = '';
    file.value = '';
    this.upload(file);
  }, false);

  // 普通文件
  async function upload(file) {
    let formData = new FormData();
    formData.append('file', file);
    let data = await axios({
      url: baseUrl + '/testApi/uploadImg',
      method: 'post',
      data: formData,
      onUploadProgress: function (progressEvent) {
        current.style.width = Math.round(progressEvent.loaded / progressEvent.total * 100) + '%';
      }
    });
    if (data.data.code == 200) {
      links.href = data.data.data;
    } else {
      alert('上传失败！')
    }
  }

  function suffix(file_name) {
    var result = /\.[^\.]+/.exec(file_name);
    return result;
  }
  // 监听大文件上传
  bigFile.addEventListener('change', (e) => {
    let file = e.target.files[0];
    console.log(file)
    // if (file.type.indexOf('application') == -1) {
    //   return alert('文件格式只能是文档应用！');
    // }
    // if ((file.size / (1000 * 1000)) > 100) {
    //   return alert('文件不能大于100MB！');
    // }
    ext = suffix(file.name)
    uploadBig(file);
  }, false);

  function uploadBig(file) {
    fileArr = sliceFile(file)
    console.log(fileArr)
    md5File(fileArr).then(res => {
      md5Val = res
      checkFile()
    })
  }

  // 切割文件
  function sliceFile(file) {
    const files = [];
    const chunkSize = 10 * 1024 * 1024;
    for (let i = 0; i < file.size; i += chunkSize) {
      const end = i + chunkSize >= file.size ? file.size : i + chunkSize;
      let currentFile = file.slice(i, (end > file.size ? file.size : end));
      files.push(currentFile);
    }
    return files;
  }

  // 获取文件md5值
  function md5File(files) {
    const spark = new SparkMD5.ArrayBuffer();
    let fileReader;
    for (var i = 0; i < files.length; i++) {
      fileReader = new FileReader();
      fileReader.readAsArrayBuffer(files[i]);
    }
    return new Promise((resolve) => {
      fileReader.onload = function (e) {
        spark.append(e.target.result);
        if (i == files.length) {
          resolve(spark.end());
        }
      }
    })
  }

  async function checkFile() {
    let data = await axios({
      url: `${baseUrl}/testApi/uploadBigFile?uploadType=bigFile&type=check&current=0&md5Val=${md5Val}&total=${fileArr.length}&ext=${ext}`,
      method: 'post'
    })
    console.log(data)
    if (data.data.code == 221) {
      uploadSlice(data.data.data)
    } else if (data.data.code == 220) { // 文件秒传成功
      alert('文件秒传成功');
      bigCurrent.style.width = '100%';
      bigLinks.href = data.data.data
    }

  }


  async function uploadSlice(chunkIndex = 0) {
    let formData = new FormData();
    formData.append('bigFile', fileArr[chunkIndex]);
    let data = await axios({
      url: `${baseUrl}/testApi/uploadBigFile?uploadType=bigFile&type=upload&current=${chunkIndex}&md5Val=${md5Val}&total=${fileArr.length}&ext=${ext}`,
      method: 'post',
      data: formData,
    })

    if (data.data.code == 200) { // 文件上传成功
      if (chunkIndex < fileArr.length - 1) {
        bigCurrent.style.width = Math.round((chunkIndex + 1) / fileArr.length * 100) + '%';
        ++chunkIndex;
        uploadSlice(chunkIndex);
      } else {
        mergeFile();
      }
    } else if (data.data.code == 221) {
      chunkIndex = data.data.data
      console.log("继续上传,继续上传切片：" + data.data.data)
      bigCurrent.style.width = Math.round((data.data.data + 1) / fileArr.length * 100) + '%';
      ++chunkIndex;
      uploadSlice(chunkIndex);
    }
  }

  async function mergeFile() {
    let data = await axios.post(
      `${baseUrl}/testApi/uploadBigFile?uploadType=bigFile&type=merge&md5Val=${md5Val}&total=${fileArr.length}&ext=${ext}`
    );
    if (data.data.code == 200) {
      alert('上传成功！');
      bigCurrent.style.width = '100%';
      bigLinks.href = data.data.data
    } else {
      alert(data.data.msg);
    }
  }
</script>

</html>